package com.seguriapp.complaints.gwt.client;

import gwtupload.client.IUploadStatus.Status;
import gwtupload.client.IUploader;
import gwtupload.client.IUploader.UploadedInfo;
import gwtupload.client.MultiUploader;
import gwtupload.client.PreloadedImage;
import gwtupload.client.PreloadedImage.OnLoadPreloadedImageHandler;

import java.util.Date;
import java.util.List;

import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.geolocation.client.Geolocation;
import com.google.gwt.geolocation.client.Position;
import com.google.gwt.geolocation.client.PositionError;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.SmallMapControl;
import com.google.gwt.maps.client.event.MapClickHandler;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.Size;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.Overlay;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.DatePicker;
import com.seguriapp.common.gwt.client.UtilClientValidations;
import com.seguriapp.complaints.gwt.client.activity.RegisterComplaintActivity;
import com.seguriapp.complaints.gwt.client.activity.RegisterComplaintActivity.Display;
import com.seguriapp.entities.Complaint;
import com.seguriapp.entities.CrimeCategory;
import com.seguriapp.entities.MediaContent;
import com.seguriapp.entities.MediaContentType;
import com.seguriapp.entities.SeguriAppEntity;

public class RegisterComplaintPage extends Composite implements Display {

  private static RegisterComplaintPageUiBinder uiBinder = GWT.create(RegisterComplaintPageUiBinder.class);
  private final static String SUCCESS_MSG = "La denuncia ha sido registrada satisfactoriamente.";
  private final static String ERROR_MSG = "Ha ocurrido un error en el registro de la denuncia, intente nuevamente por favor.";
  private final static String ERROR_MEDIA_MSG = "Ha ocurrido un error en el registro de la media de la denuncia, la denuncia fue registrada correctamente.";

  interface RegisterComplaintPageUiBinder extends UiBinder<Widget, RegisterComplaintPage> {
  }

  @UiField
  protected TextBox names;
  @UiField
  protected TextBox lastNames;
  @UiField
  protected TextBox dni;
  @UiField
  protected TextBox address;
  @UiField
  protected TextBox phone;
  @UiField
  protected TextBox email;
  @UiField
  protected TextBox crimeDate;
  @UiField
  protected ListBox crimeHour;
  @UiField
  protected ListBox crimeMinute;
  @UiField
  protected ListBox crimeTime;
  @UiField
  protected ListBox policeStation;
  @UiField
  protected ListBox crimeCategory;
  @UiField
  protected TextArea comment;
  @UiField
  protected HTMLPanel mapPanel;
  @UiField
  protected InlineLabel namesErrorMsg;
  @UiField
  protected InlineLabel lastNamesErrorMsg;
  @UiField
  protected InlineLabel dniErrorMsg;
  @UiField
  protected InlineLabel addressErrorMsg;
  @UiField
  protected InlineLabel phoneErrorMsg;
  @UiField
  protected InlineLabel emailErrorMsg;
  @UiField
  protected InlineLabel crimeDateErrorMsg;
  @UiField
  protected InlineLabel policeStationErrorMsg;
  @UiField
  protected InlineLabel crimeCategoryErrorMsg;
  @UiField
  protected InlineLabel commentErrorMsg;
  @UiField
  protected InlineLabel mapErrorMsg;
  @UiField
  protected FlowPanel panelImages;
  @UiField
  protected FlowPanel uploadImages;
  @UiField
  protected HTMLPanel registerComplaintForm;
  @UiField
  protected HTMLPanel registerComplaintMediaForm;
  @UiField
  protected HTMLPanel registerComplaintResult;
  @UiField
  protected HTMLPanel mediaGalleryUpload;
  @UiField
  protected InlineLabel resultMessageLbl;
  @UiField
  protected Button saveComplaintMedia;
  @UiField
  protected TextBox complaintIdText;

  private PopupPanel popupCalendarPanel;
  private DatePicker startDatePicker;
  private MapWidget map;
  private double latitude;
  private double longitude;
  private double userLatitude;
  private double userLongitude;
  private ErrorMessages errorMessages = null;

  private RegisterComplaintActivity presenter;

  // Load the image in the document and in the case of success attach it to the
  // viewer
  private IUploader.OnFinishUploaderHandler onFinishUploaderHandler = new IUploader.OnFinishUploaderHandler() {

    public void onFinish(IUploader uploader) {
      if (uploader.getStatus() == Status.SUCCESS) {

        // The server sends useful information to the client by default
        UploadedInfo info = uploader.getServerInfo();
        System.out.println("File name " + info.name);
        System.out.println("File content-type " + info.ctype);
        System.out.println("File size " + info.size);

        // You can send any customized message and parse it
        System.out.println("Server message " + info.message);
        final MediaContent media = new MediaContent(MediaContentType.getEnumByString(info.ctype.substring(0, info.ctype.indexOf("/"))), info.message);
        presenter.addFileToList(media);

        new PreloadedImage(uploader.fileUrl(), media.getContent(), media.getContent(), showImage);

      }
    }
  };

  // Attach an image to the pictures viewer
  private OnLoadPreloadedImageHandler showImage = new OnLoadPreloadedImageHandler() {

    public void onLoad(final PreloadedImage image) {
      image.setWidth("75px");

      final Anchor link = new Anchor("remover");
      link.addClickHandler(new ClickHandler() {

        public void onClick(ClickEvent event) {
          presenter.removeMedia(image.getName());
        }
      });
      panelImages.add(image);
      panelImages.add(link);
    }
  };

  public RegisterComplaintPage() {
    initWidget(uiBinder.createAndBindUi(this));
    initializeTimeAndDateComponents();
    crimeDate.addFocusHandler(new FocusHandler() {

      public void onFocus(FocusEvent event) {
        displayCalendar(crimeDate, startDatePicker);
      }

    });
    startDatePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {

      public void onValueChange(ValueChangeEvent<Date> event) {
        showSelectedDate(crimeDate, event.getValue());
      }

    });
    errorMessages = GWT.create(ErrorMessages.class);
    final MultiUploader defaultUploader = new MultiUploader();
    defaultUploader.addOnFinishUploadHandler(onFinishUploaderHandler);
    defaultUploader.setValidExtensions(".jpg", ".jpeg", ".mp3", ".mp4");
    uploadImages.add(defaultUploader);
  }

  public void setPresenter(final RegisterComplaintActivity presenter) {
    this.presenter = presenter;
  }

  public void setCrimeCategoryList(final List<CrimeCategory> crimeCategoryList) {
    crimeCategory.clear();
    if (crimeCategoryList != null) {
      for (final CrimeCategory crimeCategoryItem : crimeCategoryList) {
        crimeCategory.addItem(crimeCategoryItem.getName(), crimeCategoryItem.getId().toString());
      }
    }
  }

  public void setPoliceStationList(final List<SeguriAppEntity> policeStationList) {
    policeStation.clear();
    if (policeStationList != null) {
      for (final SeguriAppEntity policeStationItem : policeStationList) {
        policeStation.addItem(policeStationItem.getTitle(), policeStationItem.getId().toString());
      }
    }
  }

  @UiHandler("saveComplaint")
  protected void onSaveComplaintClick(final ClickEvent event) {
    registerComplaint(false);
  }

  @UiHandler("saveComplaintGoMedia")
  protected void onSaveComplaintGoMediaClick(final ClickEvent event) {
    registerComplaint(true);
  }

  @UiHandler("cleanData")
  protected void onCleanDataClick(final ClickEvent event) {
    cleanFormFields();
  }

  @UiHandler("saveComplaintMedia")
  protected void onSaveComplaintMediaClick(final ClickEvent event) {
    presenter.saveComplaintMedia(Long.valueOf(complaintIdText.getText()));
  }

  private void registerComplaint(final boolean addMedia) {
    if (validateFields()) {
      final Complaint complaint = new Complaint();
      complaint.setNames(names.getText());
      complaint.setLastNames(lastNames.getText());
      complaint.setDni(dni.getText());
      complaint.setAddress(address.getText());
      complaint.setEmail(email.getText());
      complaint.setPhone(phone.getText());
      complaint.setDescription(comment.getText());
      complaint.setCrimeDate(UtilClientValidations.DATE_FORMAT.parse(crimeDate.getText()));
      complaint.setMinute(Integer.valueOf(crimeMinute.getValue(crimeMinute.getSelectedIndex())));
      if (UtilClientValidations.AM_TIME_CONSTANT.equals(crimeTime.getValue(crimeTime.getSelectedIndex()))) {
        complaint.setHour(Integer.valueOf(crimeHour.getValue(crimeHour.getSelectedIndex())));
      } else {
        complaint.setHour(Integer.valueOf(crimeHour.getValue(crimeHour.getSelectedIndex())));
      }
      complaint.setCrimeLatitude(latitude);
      complaint.setCrimeLongitude(longitude);
      complaint.setComplaintLatitude(userLatitude);
      complaint.setComplaintLongitude(userLongitude);

      final CrimeCategory category = new CrimeCategory();
      category.setId(Long.valueOf(crimeCategory.getValue(crimeCategory.getSelectedIndex())));
      complaint.setCategory(category);

      final SeguriAppEntity policeStationEntity = new SeguriAppEntity();
      policeStationEntity.setId(Long.valueOf(policeStation.getValue(policeStation.getSelectedIndex())));
      complaint.setPoliceStation(policeStationEntity);
      presenter.saveComplaint(complaint, addMedia);
    }
  }

  private void initializeTimeAndDateComponents() {
    startDatePicker = new DatePicker();
    popupCalendarPanel = new PopupPanel(true);
    popupCalendarPanel.addStyleName("calendar");
    crimeHour.clear();
    for (Integer hour : UtilClientValidations.HOURS_OF_DAY) {
      crimeHour.addItem(hour.toString());
    }
    crimeMinute.clear();
    for (Integer minute : UtilClientValidations.MINUTES_OF_HOUR) {
      crimeMinute.addItem(minute.toString());
    }
    crimeTime.clear();
    crimeTime.addItem(UtilClientValidations.AM_TIME_CONSTANT);
    crimeTime.addItem(UtilClientValidations.PM_TIME_CONSTANT);
  }

  private void showSelectedDate(final TextBox textBox, final Date date) {
    final String dateStr = DateTimeFormat.getFormat("dd/MM/yyyy").format(date);
    textBox.setText(dateStr);
    popupCalendarPanel.hide();
  }

  private void displayCalendar(final TextBox textBox, final DatePicker datePicker) {
    if (popupCalendarPanel.isShowing()) {
      popupCalendarPanel.hide();
    }
    popupCalendarPanel.clear();
    popupCalendarPanel.add(datePicker);
    popupCalendarPanel.setPopupPosition(textBox.getAbsoluteLeft(), textBox.getAbsoluteTop());
    popupCalendarPanel.show();
  }

  private boolean validateFields() {
    boolean valid = true;
    if (UtilClientValidations.isEmptyString(names.getText())) {
      namesErrorMsg.setText(errorMessages.fieldEmptyMsg("Nombres"));
      valid = false;
    } else {
      namesErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(lastNames.getText())) {
      lastNamesErrorMsg.setText(errorMessages.fieldEmptyMsg("Apellidos"));
      valid = false;
    } else {
      lastNamesErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(dni.getText())) {
      dniErrorMsg.setText(errorMessages.fieldEmptyMsg("DNI"));
      valid = false;
    } else if (!UtilClientValidations.validateDNI(dni.getText())) {
      dniErrorMsg.setText(errorMessages.numberInvalidFormat());
      valid = false;
    } else {
      dniErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(address.getText())) {
      addressErrorMsg.setText(errorMessages.fieldAddressEmptyMsg());
      valid = false;
    } else {
      addressErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(phone.getText())) {
      phoneErrorMsg.setText(errorMessages.fieldPhoneEmptyMsg());
      valid = false;
    } else if (!UtilClientValidations.validateInteger(phone.getText())) {
      phoneErrorMsg.setText(errorMessages.numberInvalidFormat());
      valid = false;
    } else {
      phoneErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(email.getText())) {
      emailErrorMsg.setText(errorMessages.fieldEmptyMsg("Email"));
      valid = false;
    } else if (!UtilClientValidations.validateEmail(email.getText().toLowerCase())) {
      emailErrorMsg.setText(errorMessages.emailInvalidFormat());
      valid = false;
    } else {
      emailErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(crimeDate.getText())) {
      crimeDateErrorMsg.setText(errorMessages.fieldEmptyMsg("Fecha de delito"));
      valid = false;
    } else if (!UtilClientValidations.validateDate(crimeDate.getText())) {
      crimeDateErrorMsg.setText(errorMessages.dateInvalidFormat());
      valid = false;
    }  else if (!UtilClientValidations.validateDateValue(crimeDate.getText())) {
      crimeDateErrorMsg.setText(errorMessages.dateInvalidValue());
      valid = false;
    } else {
      crimeDateErrorMsg.setText(null);
    }
    if (UtilClientValidations.isEmptyString(comment.getText())) {
      commentErrorMsg.setText(errorMessages.fieldEmptyMsg("Denuncia"));
      valid = false;
    } else {
      commentErrorMsg.setText(null);
    }
    if (policeStation.getSelectedIndex() < 0) {
      policeStationErrorMsg.setText(errorMessages.fieldEmptyMsg("Comisar&iacute;a"));
      valid = false;
    } else {
      policeStationErrorMsg.setText(null);
    }
    if (crimeCategory.getSelectedIndex() < 0) {
      crimeCategoryErrorMsg.setText(errorMessages.fieldEmptyMsg("Categor&iacute;a"));
      valid = false;
    } else {
      crimeCategoryErrorMsg.setText(null);
    }
    if (latitude == 0 || longitude == 0) {
      mapErrorMsg.setText(errorMessages.fieldLocationEmptyMsg());
      valid = false;
    } else {
      mapErrorMsg.setText(null);
    }
    return valid;
  }

  public void initializeMapComponents(final double defaultLatitude, final double defaultLongitude, final int defaultZoom) {
    final MapOptions mapOptions = MapOptions.newInstance();
    mapOptions.setSize(Size.newInstance(800, 400));

    map = new MapWidget(LatLng.newInstance(defaultLatitude, defaultLongitude), defaultZoom, mapOptions);
    map.setScrollWheelZoomEnabled(true);
    map.addControl(new SmallMapControl());
    map.addMapClickHandler(new MapClickHandler() {

      public void onClick(MapClickEvent e) {
        final MapWidget sender = e.getSender();
        final Overlay overlay = e.getOverlay();
        final LatLng point = e.getLatLng();

        if (overlay != null && overlay instanceof Marker) {
          sender.removeOverlay(overlay);
          latitude = 0;
          longitude = 0;
        } else {
          sender.clearOverlays();
          sender.addOverlay(new Marker(point));
          latitude = point.getLatitude();
          longitude = point.getLongitude();
        }
      }
    });
    getUserLocation();
    mapPanel.clear();
    mapPanel.add(map);
  }

  public void cleanFormFields() {
    names.setText(null);
    lastNames.setText(null);
    dni.setText(null);
    address.setText(null);
    phone.setText(null);
    email.setText(null);
    crimeDate.setText(null);
    crimeHour.setSelectedIndex(crimeHour.getItemCount() > 0 ? 0 : -1);
    crimeMinute.setSelectedIndex(crimeMinute.getItemCount() > 0 ? 0 : -1);
    crimeTime.setSelectedIndex(crimeTime.getItemCount() > 0 ? 0 : -1);
    crimeCategory.setSelectedIndex(crimeCategory.getItemCount() > 0 ? 0 : -1);
    policeStation.setSelectedIndex(policeStation.getItemCount() > 0 ? 0 : -1);
    comment.setText(null);
    latitude = 0;
    longitude = 0;
    if (map != null) {
      map.clearOverlays();
    }
    panelImages.clear();
  }

  public void showRegisteredComplaint(final Long complaintId, final boolean addMedia) {
    if (complaintId == null) {
      setPanelsVisibility(false, false, true);
      resultMessageLbl.setText(ERROR_MSG);
    } else {
      complaintIdText.setText(complaintId.toString());
      setPanelsVisibility(false, addMedia, !addMedia);
      resultMessageLbl.setText(complaintId != null ? SUCCESS_MSG : ERROR_MSG);
    }
  }

  public void setPanelsVisibility (final boolean formVisible, final boolean mediaVisible, final boolean resultVisible) {
    registerComplaintForm.setVisible(formVisible);
    registerComplaintMediaForm.setVisible(mediaVisible);
    registerComplaintResult.setVisible(resultVisible);
  }

  public void removePreviewImageAndLink(final String name) {
    int index = -1;
    for (int i = 0; i < panelImages.getWidgetCount(); i++) {
      if (panelImages.getWidget(i) instanceof PreloadedImage && ((PreloadedImage) panelImages.getWidget(i)).getName().equals(name)) {
        index = i;
        break;
      }
    }
    if (index >= 0) {
      panelImages.remove(index);
      panelImages.remove(index);
    }
  }

  private void getUserLocation() {
    final Geolocation geolocation = Geolocation.getIfSupported();
    geolocation.getCurrentPosition(new Callback<Position, PositionError>() {

      public void onSuccess(Position result) {
        map.setCenter(LatLng.newInstance(result.getCoordinates().getLatitude(), result.getCoordinates().getLongitude()));
        userLongitude = result.getCoordinates().getLongitude();
        userLatitude = result.getCoordinates().getLatitude();
      }

      public void onFailure(PositionError reason) {
        Window.alert("No se pudo obtener la ubicaci&oacute;n del usuario");
      }
    });
  }

  public void showRegisteredComplaintMedia(final boolean success) {
    resultMessageLbl.setText(success ? SUCCESS_MSG : ERROR_MEDIA_MSG);
    setPanelsVisibility(false, false, true);
  }

}
